package com.atguigu.yygh.cmn.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.atguigu.yygh.cmn.readlistener.DictReadListener;
import com.atguigu.yygh.cmn.mapper.DictMapper;
import com.atguigu.yygh.cmn.service.DictService;
import com.atguigu.yygh.model.cmn.Dict;
import com.atguigu.yygh.vo.cmn.DictEeVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    @Autowired
    DictReadListener dictReadListener;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    DictMapper dictMapper;


    /**
     * value:redis中的命名空间(不同模块的缓存,放在不同的命名空间下,用于区分不同的模块的缓存)
     * 该命名空间下的缓存key的生成规则:getById_1  getById_10000
     * 根据key去redis中查询对应的value,如果value存在,直接将redis中的value返回(方法体就不执行)
     * 反之,redis中不存在,方法体就会执行,方法体执行完之后,会将return的数据作为value,存储到redis中
     */
    @Cacheable(value = "dict",key = "'getById_'+#id")
    @Override
    public List<Dict> dictList(Long id) {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id",id);
        List<Dict> dictList = baseMapper.selectList(queryWrapper);
        dictList.forEach(dict -> {
            dict.setHasChildren(this.hasChildren(dict));
        });
        return dictList;
    }

    //@Override
    public List<Dict> dictList1(Long id) {
        //1.先判断redis中是否存在该id对应的数据集合
/**

        List<Dict> list = (List<Dict>) redisTemplate.boundValueOps(id).get();
        if(list!=null&&list.size()>0){
            System.out.println("走redis");
            return list;
        }
*/
        String value = stringRedisTemplate.boundValueOps(String.valueOf(id)).get();
        List<Dict> list = JSON.parseArray(value, Dict.class);
        if(list!=null&&list.size()>0){
            System.out.println("走redis");
            return list;
        }

        //2.redis中不存在缓存数据,需要从mysql中查询
        //select* from 'dict' where parent_id=1
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id",id);
        List<Dict> dictList = baseMapper.selectList(queryWrapper);
        //每个dict对象都需要有一个hasChildren属性,所以就需要在Dict类中定义一个额外的属性(数据库表中不存在的)
        //判断当前每个dict是否存在下级,如果存在设置true,否则设置成false
        dictList.forEach(dict -> {
            dict.setHasChildren(this.hasChildren(dict));
        });
        System.out.println("走mysql");


        /**
         * 3.存入到redis,使用参数id作为key,dictList集合作为value(每一组缓存,过期时间为5分钟)
         *    redisTemplate.boundValueOps(id).set(dictList,5, TimeUnit.MINUTES);
         * 注意:
         * (1).stringRedisTemplate方法的key必须是String类型的
         * (2).value也需要string类型(将list集合转成json数组字符串)
         * (3).从redis中读取数据时,使用的template对象必须和存数据时的template对象一致
         */
        if(dictList!=null&&dictList.size()>0){
            String jsonString = JSON.toJSONString(dictList);
            stringRedisTemplate.boundValueOps(String.valueOf(id)).set(jsonString,5,TimeUnit.MINUTES);
        }
        return dictList;
    }
        private boolean hasChildren(Dict dict) {
            //select * from dict where parent_id=86
            QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("parent_id",dict.getId());
            Integer n = baseMapper.selectCount(queryWrapper);
            return n>0;

        }

    /**
     * 读取文件
     *  @CacheEvict注解的作用是每次加入新的数据就会把指定的命名空间进行删除
     * value: 删除哪一个命名空间下的缓存
     * beforeInvocation: 方法执行之前删除
     * allEntries: 是否全部删除
     * 方法体每次都会执行,并且执行方法体之前,删除dict命名空间下所有的缓存
     */
    @CacheEvict(value = "dict",beforeInvocation = true,allEntries = true)
    @Override
    public void importData(MultipartFile file) {
        //利用MultipartFile可以将文件数据读取到
        try {
            /**
             * InputStream inputStream=new FileInputStream("E:\\zhuomian\\国家数据字典.xlsx");
             * 就不需要这样去定义一个路径了
             *
             *  InputStream inputStream=file.getInputStream();
             *  file: 表示通过MultipartFile上传的文件
             *  getInputStream: 表示文件的输入流
             *
             *   EasyExcel.read(inputStream, DictEeVo.class,new DictReadListener())
             *   不可以写new DictReadListener() 如果该对象是你自己new的,他和spring没有关系,
             *   spring不会注入dictMapper
             */

            InputStream inputStream=file.getInputStream();
            EasyExcel.read(inputStream, DictEeVo.class,dictReadListener)
                    .sheet()
                    .doRead();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 在客户端下载Excel文件
     * @param response
     */
    @Override
    public void exportData(HttpServletResponse response) {
        try {
            //1.查询出所有的数据字典
            List<Dict> dictList= baseMapper.selectList(null);

            //2.类型转换(Dict转成DictEeVo)  也就是List<Dict>转成List<DictEeVo>
        /*
        List<DictEeVo> dictEeVoList=new ArrayList<>();
        //  以流的形式
        dictList.stream().forEach(dict -> {
            DictEeVo dictEeVo=new DictEeVo();
            BeanUtils.copyProperties(dict,dictEeVo);
            dictEeVoList.add(dictEeVo);
        });
        */


            /**
             * toJSONString
             * 这个方法会将dictList中的每个字典对象转换为对应的JSON对象，
             * 并将它们组合成一个JSON数组。
             * 将list集合转成json数组字符串[{},{},{}]
             */
            //2.类型转换(Dict转成DictEeVo)  也就是List<Dict>转成List<DictEeVo>
            String jsonString = JSON.toJSONString(dictList);
            /**
             * parseArray
             * 这个方法会将JSON字符串中的 每个JSON对象 解析为DictEeVo对象，
             * 并将它们添加到dictEeVos列表中。
             * DictEeVo.class表格中的数据
             */
            List<DictEeVo> dictEeVoList = JSON.parseArray(jsonString, DictEeVo.class);

            //3.文件下载需要设置的响应头(固定的步骤)
            response.setContentType("application/vnd.ms-excel"); //①文档类型
            //response.addHeader("Content-Type","application/vnd.ms-excel")  //两行代码的作用一致
            String filename="数据字典"; //浏览器下载文件的文件名
            filename=URLEncoder.encode(filename, "utf-8");//字符串按照指定格式编码
            // response.setHeader("Content-disposition", "attachment;filename="+ filename + ".xlsx");
            response.addHeader("Content-disposition", "attachment;filename="+ filename + ".xlsx");

            ServletOutputStream outputStream = response.getOutputStream();
            EasyExcel.write(outputStream,DictEeVo.class).sheet("全部字典数据").doWrite(dictEeVoList);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     *方法体每次都会执行,并且每执行一次,都会生成一组key-value,添加到指定的命名空间
     * 可用于更新某一组k-v缓存
     */
    @CachePut(value = "test_dict",key = "'dict_'+#dict.id")
    @Override
    public Dict saveDict(Dict dict) {
        System.out.println("方法体被执行");
        baseMapper.updateById(dict);
        return dict;
    }

    @Override
    public String getName(String value) {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("value",value);
        Dict dict = baseMapper.selectOne(queryWrapper);
        return dict.getName();
    }

    @Override
    public String getName(String value, String dictCode) {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("value",value);
        queryWrapper.eq("parent_id",baseMapper.selectOne(new QueryWrapper<Dict>().eq("dict_code",dictCode)).getId());

        String name = baseMapper.selectOne(queryWrapper).getName();

        return name;
    }

    @Override
    public List<Dict> findByDictCode(String dictCode) {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id",baseMapper.selectOne(new QueryWrapper<Dict>().eq("dict_code",dictCode)).getId());
        List<Dict> list = baseMapper.selectList(queryWrapper);
        return list;
    }

}
